!! Copyright (C) 2009,2010,2011,2012  Marco Restelli
!!
!! This file is part of:
!!   LDGH -- Local Hybridizable Discontinuous Galerkin toolkit
!!
!! LDGH is free software: you can redistribute it and/or modify it
!! under the terms of the GNU General Public License as published by
!! the Free Software Foundation, either version 3 of the License, or
!! (at your option) any later version.
!!
!! LDGH is distributed in the hope that it will be useful, but WITHOUT
!! ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
!! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
!! License for more details.
!!
!! You should have received a copy of the GNU General Public License
!! along with LDGH. If not, see <http://www.gnu.org/licenses/>.
!!
!! author: Marco Restelli                   <marco.restelli@gmail.com>


module mod_fu_manager
!General comments:
!  This module contains subroutines for safe management of file units.
!  It is guaranteed that an already connected unit number is not
!  redirected to a second file.
!
! Method:
!  Unit numbers are generated by cycling on the interval 10-99. Before
!  a unit number is assigned, it is tested to verify whether already
!  connected to a file. This provide a safe mechanism for managing
!  file units, and prevents from clashing with preexistent modules. No
!  file unit should be explicitly declared in any module: always
!  define an integer variable and assign it an (opaque) value by this
!  module's routines.
!
! !REVISION HISTORY:   
!  Initial version by Marco Restelli (2007-15-11).
!-----------------------------------------------------------------------

!-----------------------------------------------------------------------

 use mod_messages, only: &
   mod_messages_initialized, &
   error,   &
   warning, &
   info

!-----------------------------------------------------------------------
 
 implicit none

!-----------------------------------------------------------------------

! Module interface

 public :: &
   mod_fu_manager_constructor, &
   mod_fu_manager_destructor,  &
   mod_fu_manager_initialized, &
   new_file_unit

 private

!-----------------------------------------------------------------------

! Module types and parameters

 ! public members
 
 ! private members
 integer, parameter :: &
  low_limit = 10, &
  top_limit = 99

! Module variables

 ! public members
 logical, protected :: &
   mod_fu_manager_initialized = .false.
 ! private members
 integer :: &
   actual_unit_number = low_limit

 character(len=*), parameter :: &
   this_mod_name = 'mod_fu_manager'

!-----------------------------------------------------------------------

contains

!-----------------------------------------------------------------------

 subroutine mod_fu_manager_constructor()
  character(len=*), parameter :: &
    this_sub_name = 'constructor'

   !Consistency checks ---------------------------
   if( (mod_messages_initialized.eqv..false.) ) then
     call error(this_sub_name,this_mod_name, &
                'Not all the required modules are initialized.')
   endif
   if(mod_fu_manager_initialized.eqv..true.) then
     call warning(this_sub_name,this_mod_name, &
                  'Module is already initialized.')
   endif
   !----------------------------------------------

   mod_fu_manager_initialized = .true.
 end subroutine mod_fu_manager_constructor

!-----------------------------------------------------------------------
 
 subroutine mod_fu_manager_destructor()
  character(len=*), parameter :: &
    this_sub_name = 'destructor'
   
   !Consistency checks ---------------------------
   if(mod_fu_manager_initialized.eqv..false.) then
     call error(this_sub_name,this_mod_name, &
                'This module is not initialized.')
   endif
   !----------------------------------------------

   mod_fu_manager_initialized = .false.
 end subroutine mod_fu_manager_destructor

!-----------------------------------------------------------------------
 
 subroutine new_file_unit(fu,ierr)
  integer, intent(out) :: fu, ierr
 
  logical, parameter :: debug = .false.
  character(len=*), parameter :: &
    this_sub_name = 'new_file_unit'
  logical :: busy
  integer :: previous_unit_number
  character(len=1000) :: message
  
   !$omp critical(omp_new_file_unit_critical)
   previous_unit_number = actual_unit_number
   busy = .true.
   do
    actual_unit_number = low_limit+mod(actual_unit_number+1-low_limit, &
                                       top_limit+1-low_limit)
    inquire(unit=actual_unit_number,opened=busy)
    if((busy.eqv..false.).or. & ! either a free unit or no available units
       (actual_unit_number.eq.previous_unit_number)) exit
   enddo

   if(busy.eqv..false.) then ! we have been able to find an available unit
     ierr = 0
     fu = actual_unit_number
   else
     ierr = 1
     fu = -1
     call warning(this_sub_name,this_mod_name, &
                  'No available file units.')
   endif
 
   if(debug) then
     write(message,'(A,I10,A)') 'Assigned file unit ', fu,'.'
     call info(this_sub_name,this_mod_name,message)
   endif
   !$omp end critical(omp_new_file_unit_critical)
 
 end subroutine new_file_unit
 
!-----------------------------------------------------------------------

end module mod_fu_manager

